﻿/* Autor: Achim Schnell */

using System;
using System.Collections.Generic;
using System.Linq;

namespace AppFx.Reflection.Types
{
	/// <summary>
	/// Stellt rudimentäre Informationen zu Typen bereit
	/// </summary>
	public static class TypeHelper
	{
		#region Properties

		/// <summary>
		/// Interne Liste zur Speicherung aller vordefinierten Typen (Nur Typen mit Alias)
		/// </summary>
		private static List<Type> SimpleTypes;

		/// <summary>
		/// Interne Liste zur Speicherung der Nullable-T Äquivalte der o.g. vordefinierten Typen
		/// </summary>
		private static List<Type> NullableSimpleTypes;

		#endregion

		#region Constructors

		/// <summary>
		/// Erstellt eine neue Instanz der <see cref="TypeHelper"/>-Klasse.
		/// </summary>
		static TypeHelper()
		{
			SimpleTypes = new List<Type>();
			// alle vordefinierten wert-typen in eine liste
			SimpleTypes.AddRange(
				new Type[]
				{
					typeof(bool), 
					typeof(char),
					typeof(byte), typeof(sbyte),
					typeof(decimal), typeof(double), typeof(float),
					typeof(short), typeof(ushort),
					typeof(int), typeof(uint),
					typeof(long), typeof(ulong)
				});
			NullableSimpleTypes = new List<Type>();
			Type nillable = typeof(Nullable<>);

			// die nullable-äquivalante der vordefinierten wert-typen in eine weitere liste
			NullableSimpleTypes
				.AddRange(SimpleTypes
				.Select(nT => nillable.MakeGenericType(new Type[] {nT})));

			// strings sind keine wert-typen, sondern nur immutable und können somit nicht als Nullable<string> deklariert werden.
			SimpleTypes.Add(typeof(string));
			// guid's sind zwar nicht vordefiniert - sollen aber so behandelt werden
			SimpleTypes.Add(typeof(Guid));
		}

		#endregion

		#region Methods

		/// <summary>
		/// Gibt zurück, ob der übergebene Typ zu den einfachen Typen gehört.
		/// Das sind Wer-Typen und ihre Nullable-Äquivalente, sowie String, Guid.
		/// </summary>
		/// <param name="type">Der einzuordnende Typ.</param>
		/// <returns>
		///   <c>true</c> wenn der Typ zu dein einfachen Typen gehört, andernfalls <c>false</c>.
		/// </returns>
		/// <exception cref="T:System.ArgumentNullException">Wird geworfen, wenn der übergebene Typ null ist.</exception>
		/// <example>
		/// // Das wird false ergeben:
		/// TypeHelper.IsSimple(typeof(object));
		/// // Das wird true ergeben:
		/// TypeHelper.IsSimple(typeof(byte));
		/// </example>
		public static bool IsSimple(Type type)
		{
			if (type == null)
				throw new ArgumentNullException("type");
			if (SimpleTypes.Contains(type) || NullableSimpleTypes.Contains(type))
				return true;
			else
				return false;
		}

		/// <summary>
		/// Gibt zurück, ob es sich beim übergebenen Typ um einen listenartigen Typ handelt.
		/// (Diese Funktion prüft auf die Implementierung der Schnittstelle IEnumerable)
		/// </summary>
		/// <param name="type">Der Typ der geprüft werden soll.</param>
		/// <returns>
		///   <c>true</c> wenn es sich um einen listenartigen Typen handelt, andernfalls <c>false</c>.
		/// </returns>
		/// <exception cref="T:System.ArgumentNullException">Wird geworfen, wenn der übergebene Typ null ist.</exception>
		/// <example>
		/// // Das wird false ergeben:
		/// TypeHelper.IsIEnumerable(typeof(object));
		/// // Das wird true ergeben:
		/// TypeHelper.IsIEnumerable(typeof(ArrayList));
		/// </example>
		public static bool IsIEnumerable(Type type)
		{
			if (type == null)
				throw new ArgumentNullException("type");
			// die prüfung auf die nicht-generische variante ist hier ausreichend,
			// da deren implementierung auch für generische varianten zwingend ist.
			return type.GetInterface("System.Collections.IEnumerable") != null;
		}

		#endregion
	}
}
